package com.shadow.demo.dictionary.common;


import com.shadow.demo.dictionary.common.data.Property;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.*;

/**
 * 类型工具类
 *
 * @author penggch
 */
public class TypeUtil {

    public static final Map<String, Class<?>> TYPE_SIMPLE_MAP = new HashMap<String, Class<?>>();

    public static final List<String> PROP_NAME_EXCLUDES = new ArrayList<String>();

    static {
        PROP_NAME_EXCLUDES.add("URL");

        TYPE_SIMPLE_MAP.put(boolean.class.getName(), boolean.class);
        TYPE_SIMPLE_MAP.put(boolean[].class.getName(), boolean[].class);
        TYPE_SIMPLE_MAP.put(Boolean.class.getName(), Boolean.class);
        TYPE_SIMPLE_MAP.put(Boolean[].class.getName(), Boolean[].class);

        TYPE_SIMPLE_MAP.put(byte.class.getName(), byte.class);
        TYPE_SIMPLE_MAP.put(byte[].class.getName(), byte[].class);
        TYPE_SIMPLE_MAP.put(Byte.class.getName(), Byte.class);
        TYPE_SIMPLE_MAP.put(Byte[].class.getName(), Byte[].class);

        TYPE_SIMPLE_MAP.put(char.class.getName(), char.class);
        TYPE_SIMPLE_MAP.put(char[].class.getName(), char[].class);
        TYPE_SIMPLE_MAP.put(Character.class.getName(), Character.class);
        TYPE_SIMPLE_MAP.put(Character[].class.getName(), Character[].class);

        TYPE_SIMPLE_MAP.put(short.class.getName(), short.class);
        TYPE_SIMPLE_MAP.put(short[].class.getName(), short[].class);
        TYPE_SIMPLE_MAP.put(Short.class.getName(), Short.class);
        TYPE_SIMPLE_MAP.put(Short[].class.getName(), Short[].class);

        TYPE_SIMPLE_MAP.put(int.class.getName(), int.class);
        TYPE_SIMPLE_MAP.put(int[].class.getName(), int[].class);
        TYPE_SIMPLE_MAP.put(Integer.class.getName(), Integer.class);
        TYPE_SIMPLE_MAP.put(Integer[].class.getName(), Integer[].class);

        TYPE_SIMPLE_MAP.put(float.class.getName(), float.class);
        TYPE_SIMPLE_MAP.put(float[].class.getName(), float[].class);
        TYPE_SIMPLE_MAP.put(Float.class.getName(), Float.class);
        TYPE_SIMPLE_MAP.put(Float[].class.getName(), Float[].class);

        TYPE_SIMPLE_MAP.put(long.class.getName(), long.class);
        TYPE_SIMPLE_MAP.put(long[].class.getName(), long[].class);
        TYPE_SIMPLE_MAP.put(Long.class.getName(), Long.class);
        TYPE_SIMPLE_MAP.put(Long[].class.getName(), Long[].class);

        TYPE_SIMPLE_MAP.put(double.class.getName(), double.class);
        TYPE_SIMPLE_MAP.put(double[].class.getName(), double[].class);
        TYPE_SIMPLE_MAP.put(Double.class.getName(), Double.class);
        TYPE_SIMPLE_MAP.put(Double[].class.getName(), Double[].class);

        TYPE_SIMPLE_MAP.put(String.class.getName(), String.class);
        TYPE_SIMPLE_MAP.put(String[].class.getName(), String[].class);

        TYPE_SIMPLE_MAP.put(Date.class.getName(), Date.class);
        TYPE_SIMPLE_MAP.put(Date[].class.getName(), Date[].class);

        TYPE_SIMPLE_MAP.put(java.sql.Date.class.getName(), java.sql.Date.class);
        TYPE_SIMPLE_MAP.put(java.sql.Date[].class.getName(), java.sql.Date[].class);

        TYPE_SIMPLE_MAP.put(java.sql.Time.class.getName(), java.sql.Time.class);
        TYPE_SIMPLE_MAP.put(java.sql.Time[].class.getName(), java.sql.Time[].class);

        TYPE_SIMPLE_MAP.put(java.sql.Timestamp.class.getName(), java.sql.Timestamp.class);
        TYPE_SIMPLE_MAP.put(java.sql.Timestamp[].class.getName(), java.sql.Timestamp[].class);

        TYPE_SIMPLE_MAP.put(java.math.BigDecimal.class.getName(), java.math.BigDecimal.class);
        TYPE_SIMPLE_MAP.put(java.math.BigDecimal[].class.getName(), java.math.BigDecimal[].class);

        TYPE_SIMPLE_MAP.put(java.math.BigInteger.class.getName(), java.math.BigInteger.class);
        TYPE_SIMPLE_MAP.put(java.math.BigInteger[].class.getName(), java.math.BigInteger[].class);
    }

    /**
     * 是否是简单类型
     *
     * @param cls 类型，如String/int/Integer/long/Long/Date等
     * @return true:是简单类型；false:不是简单类型
     */
    public static boolean isSimpleType(Class<?> cls) {
        return cls != null && TYPE_SIMPLE_MAP.containsKey(cls.getName());
    }

    /**
     * 是否是简单类型
     *
     * @param typeName 类型名称，如java.lang.String/int/java.lang.Integer/long/java.lang.Long/java.util.Date等
     * @return true:是简单类型；false:不是简单类型
     */
    public static boolean isSimpleType(String typeName) {
        return typeName != null && TYPE_SIMPLE_MAP.containsKey(typeName);
    }

    /**
     * 是否是简单类型
     *
     * @param type 类型，如String/int/Integer/long/Long/Date等
     * @return true:是简单类型；false:不是简单类型
     */
    public static boolean isSimpleType(Type type) {
        if (type == null) {
            return false;
        }

        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0) {
                return false;
            }
            for (Type atr : pt.getActualTypeArguments()) {
                if (!isSimpleType(atr)) {
                    return false;
                }
            }
            return true;
        } else if (type instanceof Class) {
            return isSimpleType((Class<?>) type);
        } else {
            return false;
        }
    }

    /**
     * 获取类型的Set属性
     *
     * @param cls    类型
     * @param simple 是否只取得简单类型
     * @return 属性列表
     */
    public static List<Property> getSetterProps(Class<?> cls, boolean simple) {
        List<Property> props = new ArrayList<Property>();
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            if (Modifier.isStatic(method.getModifiers())
                    || (method.getParameterTypes() == null || method.getParameterTypes().length != 1)
                    || (!method.getName().startsWith("set") || method.getName().length() < 4)) {
                continue;
            }
            Class<?> type = method.getParameterTypes()[0];
            if (simple && !isSimpleType(type)) {
                continue;
            }
            String name = method.getName().substring(3);
            if (!PROP_NAME_EXCLUDES.contains(name)) {
                name = name.substring(0, 1).toLowerCase() + name.substring(1);
            }

            Property prop = new Property();
            prop.setName(name);
            prop.setType(type.getName());
            props.add(prop);
        }
        return props;
    }

    /**
     * 获取类型的Set属性
     *
     * @param className 类型名称
     * @param simple    是否只取得简单类型
     * @return 属性列表
     */
    public static List<Property> getSetterProps(String className, boolean simple) {
        try {
            Class<?> cls = Class.forName(className);
            return getSetterProps(cls, simple);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 获取类型的Get属性
     *
     * @param cls    类型
     * @param simple 是否只取得简单类型
     * @return 属性列表
     */
    public static List<Property> getGetterProps(Class<?> cls, boolean simple) {
        List<Property> props = new ArrayList<Property>();
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            if (Modifier.isStatic(method.getModifiers())) {
                continue;
            }
            if (method.getParameterTypes() != null && method.getParameterTypes().length > 0) {
                continue;
            }
            if (method.getReturnType() == null) {
                continue;
            }
            Class<?> type = method.getReturnType();
            if (simple && !isSimpleType(type)) {
                continue;
            }
            String name = method.getName();
            if (name.startsWith("get") && name.length() > 3) {
                name = name.substring(3);
            } else if (name.startsWith("is") && name.length() > 2) {
                name = name.substring(2);
                if (!type.getName().equals(boolean.class.getName())
                        && !type.getName().equals(Boolean.class.getName())) {
                    continue;
                }
            } else {
                continue;
            }

            if (!PROP_NAME_EXCLUDES.contains(name)) {
                name = name.substring(0, 1).toLowerCase() + name.substring(1);
            }

            Property prop = new Property();
            prop.setName(name);
            prop.setType(type.getName());
            props.add(prop);
        }
        return props;
    }

    /**
     * 获取类型的Get属性
     *
     * @param className 类型名称
     * @param simple    是否只取得简单类型
     * @return 属性列表
     */
    public static List<Property> getGetterProps(String className, boolean simple) {
        try {
            Class<?> cls = Class.forName(className);
            return getGetterProps(cls, simple);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 根据自定义类型获取java类型
     *
     * @param type 自定义类型名称
     * @return
     */
    public static String getJavaType(String type) {
        String newType = "java.lang.String";
        if ("int".equalsIgnoreCase(type) || "integer".equalsIgnoreCase(type)) {
            newType = "java.lang.Integer";
        } else if ("decimal".equalsIgnoreCase(type)) {
            newType = "java.math.BigDecimal";
        } else if ("double".equalsIgnoreCase(type)) {
            newType = "java.lang.Double";
        } else if ("float".equalsIgnoreCase(type)) {
            newType = "java.lang.Float";
        } else if ("long".equalsIgnoreCase(type)) {
            newType = "java.lang.Long";
        } else if ("string".equalsIgnoreCase(type) || "nstring".equalsIgnoreCase(type)) {
            newType = "java.lang.String";
        } else if ("date".equalsIgnoreCase(type)) {
            newType = "java.util.Date";
        } else if ("time".equalsIgnoreCase(type)) {
            newType = "java.util.Date";
        } else if ("timestamp".equalsIgnoreCase(type)) {
            newType = "java.sql.Timestamp";
        } else if ("boolean".equalsIgnoreCase(type)) {
            newType = "java.lang.Boolean";
        } else if ("bool".equalsIgnoreCase(type)) {
            newType = "java.lang.Boolean";
        } else if ("clob".equalsIgnoreCase(type) || "nclob".equalsIgnoreCase(type)) {
            newType = "java.lang.String";
        } else if ("short".equalsIgnoreCase(type)) {
            newType = "java.lang.Short";
        } else if ("byte".equalsIgnoreCase(type)) {
            newType = "java.lang.Byte";
        } else if ("stringarray".equalsIgnoreCase(type)) {
            newType = String[].class.getName();
        } else if ("integerarray".equalsIgnoreCase(type)) {
            newType = Integer[].class.getName();
        } else if ("binary".equalsIgnoreCase(type)) {
            newType = Byte[].class.getName();
        }
        return newType;
    }

    /**
     * 获取JDBC类型
     *
     * @param dataType 数据类型
     * @return
     */
    public static Integer getJdbcType(String dataType) {
        if ("nstring".equals(dataType)) {
            return Types.NVARCHAR;
        } else if ("string".equals(dataType)) {
            return Types.VARCHAR;
        } else if ("integer".equals(dataType)) {
            return Types.INTEGER;
        } else if ("int".equals(dataType)) {
            return Types.INTEGER;
        } else if ("long".equals(dataType)) {
            return Types.BIGINT;
        } else if ("double".equals(dataType)) {
            return Types.DOUBLE;
        } else if ("float".equals(dataType)) {
            return Types.FLOAT;
        } else if ("date".equals(dataType)) {
            return Types.DATE;
        } else if ("timestamp".equals(dataType)) {
            return Types.TIMESTAMP;
        } else if ("time".equals(dataType)) {
            return Types.TIME;
        } else if ("binary".equals(dataType)) {
            return Types.BINARY;
        } else if ("stringarray".equals(dataType)) {
            return Types.VARCHAR;
        } else if ("integerarray".equals(dataType)) {
            return Types.INTEGER;
        } else {
            return Types.VARCHAR;
        }
    }

    public static String getDistType(String javaType) {
        Class<?> cls = forName(javaType);
        if (byte.class.getName().equals(javaType) || Byte.class.getName().equals(javaType)) {
            return "integer";
        } else if (short.class.getName().equals(javaType) || Short.class.getName().equals(javaType)) {
            return "integer";
        } else if (int.class.getName().equals(javaType) || Integer.class.getName().equals(javaType)) {
            return "integer";
        } else if (long.class.getName().equals(javaType) || Long.class.getName().equals(javaType)) {
            return "long";
        } else if (float.class.getName().equals(javaType) || Float.class.getName().equals(javaType)) {
            return "float";
        } else if (double.class.getName().equals(javaType) || Double.class.getName().equals(javaType)) {
            return "double";
        } else if (BigDecimal.class.getName().equals(javaType)) {
            return "double";
        } else if (boolean.class.getName().equals(javaType) || Boolean.class.getName().equals(javaType)) {
            return "boolean";
        } else if (String.class.getName().equals(javaType)) {
            return "string";
        } else if (Date.class.getName().equals(javaType)) {
            return "date";
        } else if (Timestamp.class.getName().equals(javaType)) {
            return "timestamp";
        } else if (Time.class.getName().equals(javaType)) {
            return "time";
        } else if (cls.isAssignableFrom(Date.class)) {
            return "date";
        } else if ("oracle.sql.TIMESTAMP".equals(javaType)) {
            return "date";
        }
        return javaType;
    }

    private static Class<?> forName(String javaType) {
        try {
            return Class.forName(javaType);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}
